"""
@Author: li
@Email: lijianqiao2906@live.com
@FileName: device_service.py
@DateTime: 2025-06-17
@Docs: 设备相关业务服务层
"""

from typing import Any

from app.models.data_enum import DeviceTypeEnum
from app.models.data_models import Area, Brand, Device, DeviceGroup, DeviceModel
from app.repositories import (
    AreaDAO,
    BrandDAO,
    DeviceDAO,
    DeviceGroupDAO,
    DeviceModelDAO,
)
from app.schemas.device import DeviceCreate, DeviceUpdate
from app.utils import LogConfig, logger, system_log

from .base_service import BaseService


class BrandService(BaseService[Brand, BrandDAO]):
    """品牌服务类"""

    def __init__(self):
        super().__init__(BrandDAO())

    async def _validate_create_data(self, data: dict) -> None:
        """品牌创建数据校验"""
        if not data.get("name"):
            raise ValueError("品牌名称不能为空")

        # 检查名称是否已存在
        existing = await self.dao.get_by_field("name", data["name"])
        if existing:
            raise ValueError(f"品牌名称 '{data['name']}' 已存在")

    async def _validate_update_data(self, data: dict, existing: Brand) -> None:
        """品牌更新数据校验"""
        if "name" in data and data["name"] != existing.name:
            # 检查新名称是否已存在
            existing_brand = await self.dao.get_by_field("name", data["name"])
            if existing_brand:
                raise ValueError(f"品牌名称 '{data['name']}' 已存在")

    @system_log(LogConfig(log_args=True, log_result=False))
    async def get_by_name(self, name: str, user: str = "system") -> Brand | None:
        """根据名称获取品牌"""
        return await self.dao.get_by_field("name", name)

    @system_log(LogConfig(log_args=True, log_result=False))
    async def search_brands(self, keyword: str, user: str = "system") -> list[Brand]:
        """搜索品牌"""
        return await self.dao.list_by_filters(filters={"name__icontains": keyword})


class DeviceModelService(BaseService[DeviceModel, DeviceModelDAO]):
    """设备型号服务类"""

    def __init__(self):
        super().__init__(DeviceModelDAO())

    async def _validate_create_data(self, data: dict) -> None:
        """设备型号创建数据校验"""
        if not data.get("name"):
            raise ValueError("型号名称不能为空")
        if not data.get("brand_id"):
            raise ValueError("品牌ID不能为空")

        # 检查同一品牌下是否已存在相同型号
        existing = await self.dao.list_by_filters(filters={"name": data["name"], "brand_id": data["brand_id"]})
        if existing:
            raise ValueError(f"品牌下已存在型号 '{data['name']}'")

    async def _validate_update_data(self, data: dict, existing: DeviceModel) -> None:
        """设备型号更新数据校验"""
        if "name" in data and data["name"] != existing.name:
            # 检查同一品牌下是否已存在相同型号
            brand_id = data.get("brand_id", existing.brand.id)
            existing_model = await self.dao.list_by_filters(filters={"name": data["name"], "brand_id": brand_id})
            if existing_model:
                raise ValueError(f"品牌下已存在型号 '{data['name']}'")

    @system_log(LogConfig(log_args=True, log_result=False))
    async def get_by_brand(self, brand_id: int, user: str = "system") -> list[DeviceModel]:
        """获取指定品牌的所有型号"""
        return await self.dao.list_by_filters(filters={"brand_id": brand_id})

    @system_log(LogConfig(log_args=True, log_result=False))
    async def search_models(self, keyword: str, user: str = "system") -> list[DeviceModel]:
        """搜索设备型号"""
        return await self.dao.list_by_filters(filters={"name__icontains": keyword})


class AreaService(BaseService[Area, AreaDAO]):
    """区域服务类"""

    def __init__(self):
        super().__init__(AreaDAO())

    async def _validate_create_data(self, data: dict) -> None:
        """区域创建数据校验"""
        if not data.get("name"):
            raise ValueError("区域名称不能为空")

        # 检查名称是否已存在
        existing = await self.dao.get_by_field("name", data["name"])
        if existing:
            raise ValueError(f"区域名称 '{data['name']}' 已存在")

    async def _validate_update_data(self, data: dict, existing: Area) -> None:
        """区域更新数据校验"""
        if "name" in data and data["name"] != existing.name:
            # 检查新名称是否已存在
            existing_area = await self.dao.get_by_field("name", data["name"])
            if existing_area:
                raise ValueError(f"区域名称 '{data['name']}' 已存在")

    @system_log(LogConfig(log_args=True, log_result=False))
    async def get_by_name(self, name: str, user: str = "system") -> Area | None:
        """根据名称获取区域"""
        return await self.dao.get_by_field("name", name)

    @system_log(LogConfig(log_args=True, log_result=False))
    async def search_areas(self, keyword: str, user: str = "system") -> list[Area]:
        """搜索区域"""
        return await self.dao.list_by_filters(filters={"name__icontains": keyword})


class DeviceGroupService(BaseService[DeviceGroup, DeviceGroupDAO]):
    """设备分组服务类"""

    def __init__(self):
        super().__init__(DeviceGroupDAO())

    async def _validate_create_data(self, data: dict) -> None:
        """设备分组创建数据校验"""
        if not data.get("name"):
            raise ValueError("分组名称不能为空")

        # 检查名称是否已存在
        existing = await self.dao.get_by_field("name", data["name"])
        if existing:
            raise ValueError(f"分组名称 '{data['name']}' 已存在")

    async def _validate_update_data(self, data: dict, existing: DeviceGroup) -> None:
        """设备分组更新数据校验"""
        if "name" in data and data["name"] != existing.name:
            # 检查新名称是否已存在
            existing_group = await self.dao.get_by_field("name", data["name"])
            if existing_group:
                raise ValueError(f"分组名称 '{data['name']}' 已存在")

    @system_log(LogConfig(log_args=True, log_result=False))
    async def get_by_name(self, name: str, user: str = "system") -> DeviceGroup | None:
        """根据名称获取设备分组"""
        return await self.dao.get_by_field("name", name)

    @system_log(LogConfig(log_args=True, log_result=False))
    async def search_groups(self, keyword: str, user: str = "system") -> list[DeviceGroup]:
        """搜索设备分组"""
        return await self.dao.list_by_filters(filters={"name__icontains": keyword})


class DeviceService(BaseService[Device, DeviceDAO]):
    """设备服务类"""

    def __init__(self):
        super().__init__(DeviceDAO())
        self.brand_dao = BrandDAO()
        self.model_dao = DeviceModelDAO()
        self.area_dao = AreaDAO()
        self.group_dao = DeviceGroupDAO()

    async def _validate_foreign_keys(self, data: dict) -> None:
        """校验外键是否存在"""
        if "brand_id" in data and not await self.brand_dao.exists(id=data["brand_id"]):
            raise ValueError(f"品牌ID '{data['brand_id']}' 不存在")
        if "model_id" in data and not await self.model_dao.exists(id=data["model_id"]):
            raise ValueError(f"型号ID '{data['model_id']}' 不存在")
        if "area_id" in data and not await self.area_dao.exists(id=data["area_id"]):
            raise ValueError(f"区域ID '{data['area_id']}' 不存在")
        if "group_id" in data and not await self.group_dao.exists(id=data["group_id"]):
            raise ValueError(f"分组ID '{data['group_id']}' 不存在")

    async def _validate_create_data(self, data: dict) -> None:
        """设备创建数据校验"""
        schema = DeviceCreate(**data)
        # 使用新的唯一性检查方法
        if await self.dao.exists_by_unique_fields(
            management_ip=str(schema.management_ip) if schema.management_ip else None,
            name=schema.name,
            serial_number=schema.serial_number,
        ):
            raise ValueError(
                f"设备已存在（管理IP、名称或序列号重复）: "
                f"ip={schema.management_ip}, name={schema.name}, sn={schema.serial_number}"
            )
        await self._validate_foreign_keys(data)

    async def _validate_update_data(self, data: dict, existing: Device) -> None:
        """设备更新数据校验"""
        schema = DeviceUpdate.model_validate(data)

        # 检查 management_ip
        if "management_ip" in data and schema.management_ip and str(schema.management_ip) != existing.management_ip:
            if await self.dao.model.filter(management_ip=str(schema.management_ip), id__not=existing.id).exists():
                raise ValueError(f"管理IP '{schema.management_ip}' 已被其他设备使用")

        # 检查 name
        if "name" in data and schema.name and schema.name != existing.name:
            if await self.dao.model.filter(name=schema.name, id__not=existing.id).exists():
                raise ValueError(f"设备名称 '{schema.name}' 已被其他设备使用")

        # 检查 serial_number
        if "serial_number" in data and schema.serial_number and schema.serial_number != existing.serial_number:
            if await self.dao.model.filter(serial_number=schema.serial_number, id__not=existing.id).exists():
                raise ValueError(f"序列号 '{schema.serial_number}' 已被其他设备使用")

        await self._validate_foreign_keys(data)

    @system_log(LogConfig(log_args=True, log_result=False))
    async def create_device_from_import(self, device_data: dict[str, Any], user: str = "system") -> Device | None:
        """
        从导入数据创建设备，自动处理外键和自定义字段。
        如果设备已存在（根据 management_ip, name, serial_number 判断），则跳过并返回 None。
        """
        # 0. 检查设备是否已存在
        management_ip = device_data.get("management_ip")
        name = device_data.get("name")
        serial_number = device_data.get("serial_number")

        if await self.dao.exists_by_unique_fields(
            management_ip=str(management_ip) if management_ip else None,
            name=str(name) if name else None,
            serial_number=str(serial_number) if serial_number else None,
        ):
            logger.info(f"设备已存在，跳过导入: name={name}, ip={management_ip}, sn={serial_number}")
            return None  # 返回 None 表示跳过

        # 1. 提取关联对象的名称
        brand_name = device_data.pop("brand_name", None)
        model_name = device_data.pop("model_name", None)
        area_name = device_data.pop("area_name", None)
        group_name = device_data.pop("device_group", None)  # 从excel来的字段是device_group
        device_type_str = device_data.pop("device_type", DeviceTypeEnum.UNKNOWN.value)

        # 2. 定义已知Device模型的字段，用于分离
        known_device_fields = {
            "name",
            "hostname",
            "management_ip",
            "port",
            "account",
            "password",
            "enable_password",
            "connection_type",
            "rack_location",
            "serial_number",
            "status",
            "description",
        }

        # 3. 分离设备自身字段和额外信息
        device_specific_data = {}
        extra_info = {}
        for key, value in device_data.items():
            if key in known_device_fields:
                device_specific_data[key] = value
            else:
                extra_info[key] = value

        # 4. 自动创建或获取关联对象
        brand, _ = await self.brand_dao.get_or_create(
            defaults={"name": brand_name, "code": brand_name.upper() if brand_name else "UNKNOWN"}, name=brand_name
        )
        area, _ = await self.area_dao.get_or_create(
            defaults={"name": area_name, "code": area_name.upper() if area_name else "UNKNOWN"}, name=area_name
        )

        # 设备分组依赖于区域
        group = None
        if group_name:
            group, _ = await self.group_dao.get_or_create(
                defaults={"name": group_name, "area_id": area.id}, name=group_name, area_id=area.id
            )

        # 设备型号依赖于品牌和设备类型
        model = None
        if model_name:
            device_type = DeviceTypeEnum(device_type_str)
            model, _ = await self.model_dao.get_or_create(
                defaults={"name": model_name, "brand_id": brand.id, "device_type": device_type},
                name=model_name,
                brand_id=brand.id,
            )

        # 5. 准备最终的设备创建数据
        final_device_data = {
            **device_specific_data,
            "brand_id": brand.id,
            "area_id": area.id,
            "device_model_id": model.id if model else None,
            "device_group_id": group.id if group else None,
            "extra_info": extra_info,  # 存储额外信息
        }

        # 6. 验证并创建设备
        # 注意：这里的 create 方法会再次调用 _validate_create_data，但因为我们已经在前面检查过了，
        # 并且是在一个事务里，所以这里不会有问题。如果担心性能，可以重构 create 方法，
        # 允许传入一个 `skip_validation` 参数。但目前这样是安全的。
        return await self.create(final_device_data, user=user)

    @system_log(LogConfig(log_args=False, log_result=False))
    async def get_all_devices_for_export(self, user: str = "system") -> list[dict[str, Any]]:
        """获取所有设备数据用于导出，包含自定义字段"""
        devices = await self.dao.list_all_with_related()
        export_data = []
        for device in devices:
            data = {
                "name": device.name,
                "management_ip": device.management_ip,
                "port": device.port,
                "account": device.account,
                "brand_name": device.brand.name if device.brand else None,
                "model_name": device.device_model.name if device.device_model else None,
                "device_type": device.device_model.device_type.value
                if device.device_model and device.device_model.device_type
                else None,
                "area_name": device.area.name if device.area else None,
                "device_group": device.device_group.name if device.device_group else None,
                "rack_location": device.rack_location,
                "serial_number": device.serial_number,
                "status": device.status.value,
                "description": device.description,
            }
            # 添加自定义字段
            if device.extra_info:
                data.update(device.extra_info)
            export_data.append(data)
        return export_data

    @system_log(LogConfig(log_args=True, log_result=False))
    async def update_device_status(self, device_id: int, status: str, user: str = "system") -> Device | None:
        """更新设备状态"""
        return await self.dao.update_device_status(device_id, status)

    @system_log(LogConfig(log_args=True, log_result=False))
    async def get_by_ip(self, management_ip: str, user: str = "system") -> Device | None:
        """根据管理IP获取设备"""
        return await self.dao.get_by_management_ip(management_ip)

    @system_log(LogConfig(log_args=True, log_result=False))
    async def search_devices(
        self,
        keyword: str | None = None,
        brand_id: int | None = None,
        area_id: int | None = None,
        status: str | None = None,
        page: int = 1,
        page_size: int = 20,
        user: str = "system",
    ) -> dict[str, Any]:
        """搜索设备"""
        return await self.dao.search_devices(
            keyword=keyword, brand_id=brand_id, area_id=area_id, status=status, page=page, page_size=page_size
        )

    @system_log(LogConfig(log_args=False, log_result=False))
    async def get_device_statistics(self, user: str = "system") -> dict[str, Any]:
        """获取设备统计信息"""
        status_count = await self.dao.get_device_status_count()
        total_count = await self.dao.count()

        return {
            "total_devices": total_count,
            "status_breakdown": status_count,
            "active_devices": status_count.get("online", 0),
            "offline_devices": status_count.get("offline", 0),
        }
